package com.xoms.front.util.pay.wx;

import java.io.UnsupportedEncodingException;

import org.apache.ibatis.reflection.wrapper.CollectionWrapper;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.xoms.front.constants.StringConstants;
import com.xoms.front.util.net.HttpManagerImpl;
import com.xoms.front.util.pay.wx.apppay.WXPayAppSignInfo;
import com.xoms.front.util.pay.wx.callback.WXPayCallbackRequest;
import com.xoms.front.util.pay.wx.callback.WXPayResponse;
import com.xoms.front.util.pay.wx.micropay.SceneInfoJsonBean;
import com.xoms.front.util.pay.wx.micropay.WXMicropayRequest;
import com.xoms.front.util.pay.wx.micropay.WXMicropayResponse;
import com.xoms.front.util.pay.wx.orderquery.WXOrderqueryRequest;
import com.xoms.front.util.pay.wx.orderquery.WXOrderqueryResponse;
import com.xoms.front.util.pay.wx.unifiedorder.WXUnifiedOrderRequest;
import com.xoms.front.util.pay.wx.unifiedorder.WXUnifiedOrderResponse;
import com.xoms.util.StringUtil;

/**
 * 微信app支付订单管理
 */
public class WXPayManager {
	private static Logger logger = org.apache.log4j.Logger.getLogger(WXPayManager.class);

	private String appId;
	private String mchId;
	public String mchKey;

	// 是否沙箱调试
	private boolean sandbox = false;

	private static final String DEVICE_INFO = "APP";
	private static final String TRADE_TYPE = "APP";

	private static WXPayManager instance;

	private WXPayManager() {
		this.appId = StringConstants.getInstance().WECHAT_APPID;
		this.mchId = StringConstants.getInstance().WECHAT_MCH_ID;
		this.mchKey = StringConstants.getInstance().WECHAT_MCH_KEY;
	}

	public static WXPayManager getInstance() {
		synchronized (WXPayManager.class) {
			if (instance == null) {
				instance = new WXPayManager();
			}
			return instance;
		}
	}

	/**
	 * 刷卡支付
	 * 
	 * @param device_info
	 *            设备号 否 String(32) 013467007045764 终端设备号(商户自定义，如门店编号)
	 * @param body
	 *            商品描述 是 String(128) image形象店-深圳腾大- QQ公仔
	 *            商品简单描述，该字段须严格按照规范传递，具体请见参数规定
	 * @param detail
	 *            商品详情 否 String(6000) 单品优惠功能字段，需要接入详见单品优惠详细说明
	 * @param attach
	 *            附加数据 否 String(127) 说明 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
	 * @param out_trade_no
	 *            商户订单号 是 String(32) 1217752501201407033233368018
	 *            商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
	 * @param total_fee
	 *            订单金额 是 Int 888 订单总金额，单位为分，只能为整数，详见支付金额
	 * @param spbill_create_ip
	 *            终端IP 是 String(16) 8.8.8.8 调用微信支付API的机器IP
	 * @param auth_code
	 *            授权码 是 String(128) 120061098828009406 扫码支付授权码，设备读取用户微信中的条码或者二维码信息
	 *            （注：用户刷卡条形码规则：18位纯数字，以10、11、12、13、14、15开头）
	 * @param store_id
	 *            门店唯一标识
	 * @param store_name
	 *            门店名称
	 * @param store_areacode
	 *            门店所在地行政区划码
	 * @param store_address
	 *            门店详细地址
	 */
	public WXMicropayResponse micropay(String device_info, String body, String detail, String attach,
			String out_trade_no, int total_fee, String spbill_create_ip, String auth_code, String store_id,
			String store_name, String store_areacode, String store_address) {
		WXMicropayRequest request = new WXMicropayRequest();
		request.setAppid(appId);
		request.setMch_id(mchId);
		request.setDevice_info(device_info);
		request.setNonce_str(WXPayUtil.generateNonceStr());
		request.setBody(body);
		request.setDetail(detail);
		request.setAttach(attach);
		request.setOut_trade_no(out_trade_no);
		request.setTotal_fee(total_fee);
		request.setFee_type("CNY");
		request.setSpbill_create_ip(spbill_create_ip);
		request.setAuth_code(auth_code);
		// 实际门店信息
		SceneInfoJsonBean scene_info = new SceneInfoJsonBean();
		scene_info.setId(store_id);
		scene_info.setName(store_name);
		scene_info.setArea_code(store_areacode);
		scene_info.setAddress(store_address);
		request.setScene_info(JSON.toJSONString(scene_info));
		request.setSign_type(WXPayConstants.MD5);
		request.setSign(WXPayUtil.getPaySign(request, mchKey));
		JaxbUtils requestBinder = new JaxbUtils(WXMicropayRequest.class, CollectionWrapper.class);
		String retXml = requestBinder.toXml(request, "utf-8");
		logger.info("创建订单 生成的XML================================\n" + retXml);
		String resultStr = HttpManagerImpl.getInstance()
				.post(WXPayConstants.HTTPS_PREFIX + WXPayConstants.DOMAIN_API
						+ (sandbox ? WXPayConstants.SANDBOX_MICROPAY_URL_SUFFIX : WXPayConstants.MICROPAY_URL_SUFFIX),
						retXml);
		try {
			resultStr = new String(resultStr.getBytes("ISO-8859-1"), "utf-8");
			logger.info("创建订单 result: \n" + resultStr);
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}
		JaxbUtils responseBinder = new JaxbUtils(WXMicropayResponse.class, CollectionWrapper.class);
		WXMicropayResponse res = responseBinder.fromXml(resultStr);
		if (res != null && WXPayConstants.SUCCESS.equals(res.getReturn_code())) {
			logger.info("success");
			return res;
		}
		return null;
	}

	public static void main(String[] args) {
		// WechatOrderManager.getInstance().micropay("013467007045764", "烟酒", "中华",
		// "附加数据", "1217752501201407033233368019",
		// 1, "192.168.1.0", "134776265141139828", "NJ_001", "X影院", "440305", "北京市海淀区");
	}

	/**
	 * 二维码 统一下单接口
	 * 
	 * @param device_info
	 *            设备号 否 String(32) 013467007045764 终端设备号(商户自定义，如门店编号)
	 * @param body
	 *            商品描述 是 String(128) image形象店-深圳腾大- QQ公仔
	 *            商品简单描述，该字段须严格按照规范传递，具体请见参数规定
	 * @param detail
	 *            商品详情 否 String(6000) 单品优惠功能字段，需要接入详见单品优惠详细说明
	 * @param attach
	 *            附加数据 否 String(127) 说明 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
	 * @param out_trade_no
	 *            商户订单号 是 String(32) 1217752501201407033233368018
	 *            商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
	 * @param total_fee
	 *            订单金额 是 Int 888 订单总金额，单位为分，只能为整数，详见支付金额
	 * @param spbill_create_ip
	 *            终端IP 是 String(16) 8.8.8.8 调用微信支付API的机器IP
	 * @param notify_url
	 *            通知地址,长度256,接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
	 * @param store_id
	 *            门店唯一标识
	 * @param store_name
	 *            门店名称
	 * @param store_areacode
	 *            门店所在地行政区划码
	 * @param store_address
	 *            门店详细地址
	 */
	public WXUnifiedOrderResponse unifiedOrder(String device_info, String body, String detail, String attach,
			String out_trade_no, int total_fee, String spbill_create_ip, String notify_url, String store_id,
			String store_name, String store_areacode, String store_address) {
		WXUnifiedOrderRequest order = new WXUnifiedOrderRequest();
		order.setAppid(appId);
		order.setMch_id(mchId);
		order.setDevice_info(device_info);
		order.setNonce_str(WXPayUtil.generateNonceStr());
		order.setBody(body);
		order.setDetail(detail);
		order.setAttach(attach);
		order.setOut_trade_no(out_trade_no);
		order.setFee_type("CNY");
		order.setTotal_fee(total_fee);
		order.setSpbill_create_ip(spbill_create_ip);
		order.setNotify_url(notify_url);
		order.setTrade_type(WXPayConstants.TRADE_TYPE_NATIVE);
		order.setProduct_id("1234567891234567890");
		// 实际门店信息
		SceneInfoJsonBean scene_info = new SceneInfoJsonBean();
		scene_info.setId(store_id);
		scene_info.setName(store_name);
		scene_info.setArea_code(store_areacode);
		scene_info.setAddress(store_address);
		order.setScene_info(JSON.toJSONString(scene_info));
		order.setSign_type(WXPayConstants.MD5);
		order.setSign(WXPayUtil.getPaySign(order, mchKey));
		logger.info("创建订单=========================================================================");
		JaxbUtils requestBinder = new JaxbUtils(WXUnifiedOrderRequest.class, CollectionWrapper.class);
		String retXml = requestBinder.toXml(order, "utf-8");
		logger.info("生成的XML=======================" + retXml);
		String resultStr = HttpManagerImpl.getInstance().post(WXPayConstants.HTTPS_PREFIX + WXPayConstants.DOMAIN_API
				+ (sandbox ? WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX : WXPayConstants.UNIFIEDORDER_URL_SUFFIX),
				retXml);
		try {
			resultStr = new String(resultStr.getBytes("ISO-8859-1"), "utf-8");
			logger.info("创建订单 ss=" + resultStr);
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}
		JaxbUtils responseBinder = new JaxbUtils(WXUnifiedOrderResponse.class, CollectionWrapper.class);
		WXUnifiedOrderResponse res = responseBinder.fromXml(resultStr);
		logger.info("生成的Object=======================" + res);
		if (res != null && WXPayConstants.SUCCESS.equals(res.getReturn_code())) {
			return res;
		}
		logger.info("error=======================" + res.getReturn_msg());
		return null;
	}

	/**
	 * app 统一下单接口
	 * 
	 * @param body
	 *            商品描述,长度128位 格式 app名-商品名称
	 * @param detail
	 *            商品详情,长度8192
	 * @param attach
	 *            附加数据,长度127 用于商户携带订单的自定义数据
	 * @param out_trade_no
	 *            商户系统内部订单号,长度32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
	 * @param total_fee
	 *            订单总金额，单位为分
	 * @param create_ip
	 *            终端IP,长度16
	 * @param notify_url
	 *            通知地址,长度256,接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
	 * 
	 */
	public WXUnifiedOrderResponse appUnifiedorder(String body, String detail, String attach, String out_trade_no,
			int total_fee, String create_ip, String notify_url) {
		WXUnifiedOrderRequest order = new WXUnifiedOrderRequest();
		order.setAppid(appId);
		order.setMch_id(mchId);
		order.setDevice_info(DEVICE_INFO);
		order.setNonce_str(WXPayUtil.generateNonceStr());
		order.setBody(body);
		order.setDetail(detail);
		order.setAttach(attach);
		order.setOut_trade_no(out_trade_no);
		order.setFee_type("CNY");
		order.setTotal_fee(total_fee);
		order.setSpbill_create_ip(create_ip);
		order.setNotify_url(notify_url);
		order.setTrade_type(TRADE_TYPE);
		order.setSign_type(WXPayConstants.MD5);
		order.setSign(WXPayUtil.getPaySign(order, mchKey));
		logger.info("创建订单=========================================================================");
		JaxbUtils requestBinder = new JaxbUtils(WXUnifiedOrderRequest.class, CollectionWrapper.class);
		String retXml = requestBinder.toXml(order, "utf-8");
		logger.info("生成的XML=======================" + retXml);
		String resultStr = HttpManagerImpl.getInstance().post(WXPayConstants.HTTPS_PREFIX + WXPayConstants.DOMAIN_API
				+ (sandbox ? WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX : WXPayConstants.UNIFIEDORDER_URL_SUFFIX),
				retXml);
		try {
			resultStr = new String(resultStr.getBytes("ISO-8859-1"), "utf-8");
			logger.info("创建订单 ss=" + resultStr);
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}
		JaxbUtils responseBinder = new JaxbUtils(WXUnifiedOrderResponse.class, CollectionWrapper.class);
		WXUnifiedOrderResponse res = responseBinder.fromXml(resultStr);
		logger.info("生成的Object=======================" + res);
		if (res != null && WXPayConstants.SUCCESS.equals(res.getReturn_code())) {
			return res;
		}
		logger.info("error=======================" + res.getReturn_msg());
		return null;
	}

	/**
	 * 生成带签名的app端支付信息
	 * 
	 * @param prepay_id
	 *            预支付交易会话ID
	 * @return
	 */
	public WXPayAppSignInfo getAppPay(String prepay_id) {
		WXPayAppSignInfo appPay = new WXPayAppSignInfo();
		appPay.setAppid(appId);
		appPay.setPartnerid(mchId);
		appPay.setPrepayid(prepay_id);
		appPay.setPackageStr("Sign=WXPay");
		appPay.setNoncestr(WXPayUtil.generateNonceStr());
		appPay.setTimestamp(String.valueOf(WXPayUtil.getNowTimestamp()));
		appPay.setSign(WXPayUtil.getPaySign(appPay, mchKey, "package"));
		logger.info("appPay=======================" + appPay);
		return appPay;
	}

	/**
	 * 支付回调解析
	 * 
	 * @param resStr
	 *            回调参数
	 */
	public WXPayCallbackRequest payCallback(String resStr) {
		JaxbUtils jaxbUtils = new JaxbUtils(WXPayCallbackRequest.class);
		if (StringUtil.isEmpty(resStr)) {
			return null;
		}
		WXPayCallbackRequest data = jaxbUtils.fromXml(resStr);
		return data;
	}

	/**
	 * 查询订单
	 * 
	 * @param transaction_id
	 * @param out_trade_no
	 */
	public WXOrderqueryResponse orderquery(String transaction_id, String out_trade_no) {
		WXOrderqueryRequest request = new WXOrderqueryRequest();
		request.setAppid(appId);
		request.setMch_id(mchId);
		if (transaction_id != null && transaction_id != "") {
			request.setTransaction_id(transaction_id);
		} else {
			request.setOut_trade_no(out_trade_no);
		}
		request.setNonce_str(WXPayUtil.generateNonceStr());
		request.setSign_type(WXPayConstants.MD5);
		request.setSign(WXPayUtil.getPaySign(request, mchKey));
		logger.info("微信订单查询创建=========================================================================");
		JaxbUtils requestBinder = new JaxbUtils(WXOrderqueryRequest.class, CollectionWrapper.class);
		String retXml = requestBinder.toXml(request, WXPayConstants.RESPONSE_ENCODE_UTF_8);
		logger.info("微信订单查询创建生成的XML=======================" + retXml);
		String resultStr = HttpManagerImpl.getInstance().post(WXPayConstants.HTTPS_PREFIX + WXPayConstants.DOMAIN_API
				+ (sandbox ? WXPayConstants.SANDBOX_ORDERQUERY_URL_SUFFIX : WXPayConstants.ORDERQUERY_URL_SUFFIX),
				retXml);
		try {
			resultStr = new String(resultStr.getBytes(WXPayConstants.RESPONSE_ENCODE),
					WXPayConstants.RESPONSE_ENCODE_UTF_8);
			logger.info("微信订单查询创建 ss=" + resultStr);
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}
		JaxbUtils responseBinder = new JaxbUtils(WXOrderqueryResponse.class, CollectionWrapper.class);
		WXOrderqueryResponse res = responseBinder.fromXml(resultStr);
		logger.info("微信订单查询创建 生成的Object=======================" + res);
		if (res != null && WXPayConstants.SUCCESS.equals(res.getReturn_code())) {
			return res;
		}
		logger.info("error=======================" + res.getReturn_msg());
		return null;
	}

	/**
	 * 支付回调-返回微信服务器失败消息
	 * 
	 * @return
	 */
	public String return2WXFail(String msg) {
		JaxbUtils requestBinder = new JaxbUtils(WXPayResponse.class, CollectionWrapper.class);
		WXPayResponse payResponse = new WXPayResponse();
		payResponse.setReturn_code(WXPayConstants.FAIL);
		if (msg == null) {
			payResponse.setReturn_msg(WXPayConstants.FAIL);
		} else {
			payResponse.setReturn_msg(msg);
		}
		String retXml = requestBinder.toXml(payResponse, "utf-8");
		return retXml;
	}

	/**
	 * 支付回调-返回微信服务器成功消息
	 * 
	 * @return
	 */
	public String return2WXSucess() {
		JaxbUtils requestBinder = new JaxbUtils(WXPayResponse.class, CollectionWrapper.class);
		WXPayResponse payResponse = new WXPayResponse();
		payResponse.setReturn_code("SUCCESS");
		payResponse.setReturn_msg("OK");
		String retXml = requestBinder.toXml(payResponse, "utf-8");
		return retXml;
	}

}
